package com.xuhao.didi.oksocket;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.xuhao.didi.core.iocore.interfaces.IPulseSendable;
import com.xuhao.didi.core.iocore.interfaces.ISendable;
import com.xuhao.didi.core.pojo.OriginalData;
import com.xuhao.didi.oksocket.adapter.LogAdapter;
import com.xuhao.didi.oksocket.btnview.Ripple;
import com.xuhao.didi.oksocket.data.DefaultSendBean;
import com.xuhao.didi.oksocket.data.HandShakeBean;
import com.xuhao.didi.oksocket.data.LogBean;
import com.xuhao.didi.oksocket.data.PulseBean;
import com.xuhao.didi.socket.client.impl.client.action.ActionDispatcher;
import com.xuhao.didi.socket.client.sdk.OkSocket;
import com.xuhao.didi.socket.client.sdk.client.ConnectionInfo;
import com.xuhao.didi.socket.client.sdk.client.OkSocketOptions;
import com.xuhao.didi.socket.client.sdk.client.action.SocketActionAdapter;
import com.xuhao.didi.socket.client.sdk.client.connection.IConnectionManager;
import com.xuhao.didi.socket.client.sdk.client.connection.NoneReconnect;
import com.xuhao.didi.socket.common.interfaces.utils.TextUtils;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.agp.utils.TextTool;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.miscservices.pasteboard.PasteData;
import ohos.miscservices.pasteboard.SystemPasteboard;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;

public class ComplexDemo extends Ability implements AbsButton.CheckedStateChangedListener,
        Component.ClickedListener, Component.FocusChangedListener {
    private ConnectionInfo mInfo;
    private Ripple mConnect;
    private IConnectionManager mManager;
    private TextField mIpEt;
    private TextField mPortEt;
    private Ripple mRedirect;
    private TextField mFrequencyEt;
    private Ripple mSetFrequency;
    private Ripple mMenualPulse;
    private Ripple mClearLog;
    private Checkbox mReconnectSwitch;
    private ListContainer mSendList;
    private ListContainer mReceList;
    private Text mPortHint;
    private Text mPortText;
    private Text mEditHint;
    private Text mIpText;
    private Text mPulseHint;
    private Text mPulseText;
    private LogAdapter mSendLogAdapter;
    private LogAdapter mReceLogAdapter;
    private String mSelectColor = "#169E93";
    private String mColorUn = "#7A7A7A";
    private int maxsize = 15;
    private int mSize = 11;
    private TextUtile textUtils;
    private SocketActionAdapter adapter = new SocketActionAdapter() {
        @Override
        public void onSocketConnectionSuccess(ConnectionInfo info, String action) {
            logRece("连接成功(Connecting Successful)");
            mManager.send(new HandShakeBean());
            mConnect.setText("DisConnect");
            initSwitch();
            mManager.getPulseManager().setPulseSendable(new PulseBean());
            mIpEt.setEnabled(true);
            mPortEt.setEnabled(true);
            textUtils.chickTextColor(ComplexDemo.this,mIpEt,mPortEt);
        }

        private void initSwitch() {
            OkSocketOptions okSocketOptions = mManager.getOption();
            mReconnectSwitch.setChecked(!(okSocketOptions.getReconnectionManager() instanceof NoneReconnect));
        }

        @Override
        public void onSocketDisconnection(ConnectionInfo info, String action, Exception e) {
            if (e != null) {
                if (e instanceof RedirectException) {
                    logSend("正在重定向连接(Redirect Connecting)...");
                    mManager.switchConnectionInfo(((RedirectException) e).redirectInfo);
                    mManager.connect();
                    mIpEt.setEnabled(true);
                    mPortEt.setEnabled(true);
                    textUtils.chickTextColor(ComplexDemo.this,mIpEt,mPortEt);
                } else {
                    logSend("异常断开(Disconnected with exception):" + e.getMessage());
                    mIpEt.setEnabled(false);
                    mPortEt.setEnabled(false);
                    textUtils.textInfo(mIpEt,mIpText,maxsize);
                    textUtils.textInfo(mPortEt,mPulseText,maxsize);
                    textUtils.chickColor(ComplexDemo.this,mIpEt,mPortEt);
                }
            } else {
                logSend("正常断开(Disconnect Manually)");
                mIpEt.setEnabled(false);
                mPortEt.setEnabled(false);
                textUtils.textInfo(mIpEt,mIpText,maxsize);
                textUtils.textInfo(mPortEt,mPulseText,maxsize);
                textUtils.chickColor(ComplexDemo.this,mIpEt,mPortEt);
            }
            mConnect.setText("Connect");
        }

        @Override
        public void onSocketConnectionFailed(ConnectionInfo info, String action, Exception e) {
            logSend("连接失败(Connecting Failed)");
            mConnect.setText("Connect");
            mIpEt.setEnabled(false);
            mPortEt.setEnabled(false);
            textUtils.textInfo(mIpEt,mIpText,maxsize);
            textUtils.textInfo(mPortEt,mPulseText,maxsize);
            textUtils.chickColor(ComplexDemo.this,mIpEt,mPortEt);
        }

        @Override
        public void onSocketReadResponse(ConnectionInfo info, String action, OriginalData data) {
            String str = new String(data.getBodyBytes(), Charset.forName("utf-8"));
            JsonObject jsonObject = new JsonParser().parse(str).getAsJsonObject();
            int cmd = jsonObject.get("cmd").getAsInt();
            if (cmd == 54) { // 登陆成功
                String handshake = jsonObject.get("handshake").getAsString();
                logRece("握手成功! 握手信息(Handshake Success):" + handshake + ". 开始心跳(Start Heartbeat)..");
            } else if (cmd == 57) { // 切换,重定向.(暂时无法演示,如有疑问请咨询github)
                String ip = jsonObject.get("data").getAsString().split(":")[0];
                int port = Integer.parseInt(jsonObject.get("data").getAsString().split(":")[1]);
                ConnectionInfo redirectInfo = new ConnectionInfo(ip, port);
                redirectInfo.setBackupInfo(mInfo.getBackupInfo());
                mManager.getReconnectionManager().addIgnoreException(RedirectException.class);
                mManager.disconnect(new RedirectException(redirectInfo));
            } else if (cmd == 14) { // 心跳
                logRece("收到心跳,喂狗成功(Heartbeat Received,Feed the Dog)");
                mManager.getPulseManager().feed();
            } else {
                logRece(str);
            }
        }

        @Override
        public void onSocketWriteResponse(ConnectionInfo info, String action, ISendable data) {
            byte[] bytes = data.parse();
            bytes = Arrays.copyOfRange(bytes, 4, bytes.length);
            String str = new String(bytes, Charset.forName("utf-8"));
            JsonObject jsonObject = new JsonParser().parse(str).getAsJsonObject();
            int cmd = jsonObject.get("cmd").getAsInt();
            switch (cmd) {
                case 54: {
                    String handshake = jsonObject.get("handshake").getAsString();
                    logSend("发送握手数据(Handshake Sending):" + handshake);
                    mManager.getPulseManager().pulse();
                    break;
                }
                default:
                    logSend(str);
            }
        }

        @Override
        public void onPulseSend(ConnectionInfo info, IPulseSendable data) {
            byte[] bytes = data.parse();
            bytes = Arrays.copyOfRange(bytes, 4, bytes.length);
            String str = new String(bytes, Charset.forName("utf-8"));
            JsonObject jsonObject = new JsonParser().parse(str).getAsJsonObject();
            int cmd = jsonObject.get("cmd").getAsInt();
            if (cmd == 14) {
                logSend("发送心跳包(Heartbeat Sending)");
            }
        }
    };

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_complex_demo);
        findViews();
        initData();
    }

    private void findViews() {
        textUtils = new TextUtile();
        mSendList = (ListContainer) findComponentById(ResourceTable.Id_send_list);
        mReceList = (ListContainer) findComponentById(ResourceTable.Id_rece_list);
        mClearLog = (Ripple) findComponentById(ResourceTable.Id_clear_log);
        mSetFrequency = (Ripple) findComponentById(ResourceTable.Id_set_pulse_frequency);
        mFrequencyEt = (TextField) findComponentById(ResourceTable.Id_pulse_frequency);
        mConnect = (Ripple) findComponentById(ResourceTable.Id_connect);
        mIpEt = (TextField) findComponentById(ResourceTable.Id_ip);
        mPortEt = (TextField) findComponentById(ResourceTable.Id_port);
        mRedirect = (Ripple) findComponentById(ResourceTable.Id_redirect);
        mMenualPulse = (Ripple) findComponentById(ResourceTable.Id_manual_pulse);
        mReconnectSwitch = (Checkbox) findComponentById(ResourceTable.Id_switch_reconnect);
        mEditHint = (Text) findComponentById(ResourceTable.Id_edit_hint);
        mIpText = (Text) findComponentById(ResourceTable.Id_ip_text);
        mPortHint = (Text) findComponentById(ResourceTable.Id_port_hint);
        mPortText = (Text) findComponentById(ResourceTable.Id_port_text);
        mPulseHint = (Text) findComponentById(ResourceTable.Id_pulse_hint);
        mPulseText = (Text) findComponentById(ResourceTable.Id_pulse_text);
        mReconnectSwitch.setCheckedStateChangedListener(this);
        mClearLog.setClickedListener(this);
        mConnect.setClickedListener(this);
        mRedirect.setClickedListener(this);
        mSetFrequency.setClickedListener(this);
        mMenualPulse.setClickedListener(this);
        mIpEt.setFocusChangedListener(this);
        mPortEt.setFocusChangedListener(this);
        mFrequencyEt.setFocusChangedListener(this);
        mSendLogAdapter = new LogAdapter(this);
        mReceLogAdapter = new LogAdapter(this);
        textField();
    }

    private void textField() {
        if (!TextUtils.isEmpty(mIpEt.getText())) {
            mEditHint.setVisibility(Component.VISIBLE);
            mIpText.setVisibility(Component.VERTICAL);
        } else {
            mEditHint.setVisibility(Component.VERTICAL);
            mIpText.setVisibility(Component.VISIBLE);
        }
        if (!TextUtils.isEmpty(mPortEt.getText())) {
            mPortHint.setVisibility(Component.VISIBLE);
            mPortText.setVisibility(Component.VERTICAL);
        } else {
            mPortHint.setVisibility(Component.VERTICAL);
            mPortText.setVisibility(Component.VISIBLE);
        }
        if (!TextUtils.isEmpty(mFrequencyEt.getText())) {
            mPulseHint.setVisibility(Component.VISIBLE);
            mPulseText.setVisibility(Component.VERTICAL);
        } else {
            mPulseHint.setVisibility(Component.VERTICAL);
            mPulseText.setVisibility(Component.VISIBLE);
        }
    }

    private void initData() {
        mIpEt.setEnabled(false);
        mPortEt.setEnabled(false);
        textUtils.textInfo(mIpEt,mIpText,maxsize);
        textUtils.textInfo(mPortEt,mPulseText,maxsize);
        textUtils.chickColor(ComplexDemo.this,mIpEt,mPortEt);
        mSendList.setItemProvider(mSendLogAdapter);
        mReceList.setItemProvider(mReceLogAdapter);
        String host = "";
        ArrayList<InetAddress> localData = getHostAddresss();
        InetAddress inetAddress = localData.get(0);
        if (TextTool.isNullOrEmpty(inetAddress.getHostAddress())) {
            host = "104.238.184.237";
        } else {
            host = inetAddress.getHostAddress();
        }
        mInfo = new ConnectionInfo(host, 8080);
        EventHandler mHandler = new EventHandler(EventRunner.getMainEventRunner());
        OkSocketOptions.Builder builder = new OkSocketOptions.Builder();
        builder.setReconnectionManager(new NoneReconnect());
        builder.setCallbackThreadModeToken(new OkSocketOptions.ThreadModeToken() {
            @Override
            public void handleCallbackEvent(ActionDispatcher.ActionRunnable runnable) {
                mHandler.postTask(runnable);
            }
        });
        mManager = OkSocket.open(mInfo).option(builder.build());
        mManager.registerReceiver(adapter);
        mSendList.setItemLongClickedListener(new ListContainer.ItemLongClickedListener() {
            @Override
            public boolean onItemLongClicked(ListContainer listContainer, Component component, int i, long l) {
                LogBean log = (LogBean) listContainer.getItemProvider().getItem(i);
                SystemPasteboard systemPasteboard = SystemPasteboard.getSystemPasteboard(ComplexDemo.this);
                PasteData pasteData = PasteData.creatPlainTextData(log.mWho);
                systemPasteboard.setPasteData(pasteData);
                textUtils.toastText("复制到剪切板", ComplexDemo.this);
                return false;
            }
        });
        mReceList.setItemLongClickedListener(new ListContainer.ItemLongClickedListener() {
            @Override
            public boolean onItemLongClicked(ListContainer listContainer, Component component, int i, long l) {
                LogBean log = (LogBean) listContainer.getItemProvider().getItem(i);
                SystemPasteboard systemPasteboard = SystemPasteboard.getSystemPasteboard(ComplexDemo.this);
                PasteData pasteData = PasteData.creatPlainTextData(log.mWho == null ? "" : log.mWho);
                systemPasteboard.setPasteData(pasteData);
                textUtils.toastText("复制到剪切板", ComplexDemo.this);
                return false;
            }
        });
    }

    private void logRece(String log) {
        LogBean logBean = new LogBean(System.currentTimeMillis(), log);
        mReceLogAdapter.getDataList().add(0, logBean);
        mReceLogAdapter.notifyDataChanged();
    }

    private void logSend(String log) {
        LogBean logBean = new LogBean(System.currentTimeMillis(), log);
        mSendLogAdapter.getDataList().add(0, logBean);
        mSendLogAdapter.notifyDataChanged();
    }

    @Override
    public void onCheckedChanged(AbsButton absButton, boolean isChecked) {
        if (!isChecked) {
            if (!(mManager.getReconnectionManager() instanceof NoneReconnect)) {
                mManager.option(new OkSocketOptions.Builder(mManager.getOption())
                        .setReconnectionManager(new NoneReconnect()).build());
                logSend("关闭重连管理器(Turn Off The Reconnection Manager)");
            }
        } else {
            if (mManager.getReconnectionManager() instanceof NoneReconnect) {
                mManager.option(new OkSocketOptions.Builder(mManager.getOption())
                        .setReconnectionManager(OkSocketOptions.getDefault().getReconnectionManager()).build());
                logSend("打开重连管理器(Turn On The Reconnection Manager)");
            }
        }
    }

    public ArrayList<InetAddress> getHostAddresss() {
        ArrayList<InetAddress> foundAddresses = new ArrayList<>();

        Enumeration<NetworkInterface> ifaces;
        try {
            ifaces = NetworkInterface.getNetworkInterfaces();
            while (ifaces.hasMoreElements()) {
                NetworkInterface iface = ifaces.nextElement();
                Enumeration<InetAddress> addresses = iface.getInetAddresses();
                while (addresses.hasMoreElements()) {
                    InetAddress addr = addresses.nextElement();
                    if (addr instanceof Inet4Address && !addr.isLoopbackAddress()) {
                        foundAddresses.add(addr);
                    }
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
        }
        return foundAddresses;
    }

    @Override
    public void onClick(Component component) {
        switch (component.getId()) {
            case ResourceTable.Id_clear_log:
                mReceLogAdapter.getDataList().clear();
                mSendLogAdapter.getDataList().clear();
                mReceLogAdapter.notifyDataChanged();
                mSendLogAdapter.notifyDataChanged();
                break;
            case ResourceTable.Id_connect:
                if (mManager == null) {
                    return;
                }
                if (!mManager.isConnect()) {
                    mManager.connect();
                } else {
                    mConnect.setText("DisConnecting");
                    mManager.disconnect();
                }
                break;
            case ResourceTable.Id_redirect:
                if (mManager == null) {
                    return;
                }
                String ip = mIpEt.getText().toString();
                String portStr = mPortEt.getText().toString();
                JsonObject jsonObject = new JsonObject();
                jsonObject.addProperty("cmd", 57);
                jsonObject.addProperty("data", ip + ":" + portStr);
                DefaultSendBean bean = new DefaultSendBean();
                bean.setContent(new Gson().toJson(jsonObject));
                mManager.send(bean);
                break;
            case ResourceTable.Id_set_pulse_frequency:
                if (mManager == null) {
                    return;
                }
                String frequencyStr = mFrequencyEt.getText();
                if (TextTool.isNullOrEmpty(frequencyStr)) {
                    return;
                }
                long frequency = 0L;
                frequency = Long.parseLong(frequencyStr);
                OkSocketOptions okOptions = new OkSocketOptions.Builder(mManager.getOption())
                        .setPulseFrequency(frequency)
                        .build();
                mManager.option(okOptions);

                break;
            case ResourceTable.Id_manual_pulse:
                if (mManager == null || mManager.getPulseManager() == null) {
                    return;
                }
                mManager.getPulseManager().trigger();
                break;
            default:
                break;
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mManager != null) {
            mManager.disconnect();
            mManager.unRegisterReceiver(adapter);
        }
    }

    @Override
    public void onFocusChange(Component component, boolean isFocus) {
        ShapeElement errorElement;
        String color = "";
        switch (component.getId()) {
            case ResourceTable.Id_ip:
                if (isFocus) {
                    color = mSelectColor;
                    errorElement = new ShapeElement(this, ResourceTable.Graphic_background_textfild_color);
                    if (mIpText.getVisibility() == Component.VISIBLE) {
                        textUtils.animatorField(mIpText, true, mSize);
                    }
                } else {
                    color = mColorUn;
                    errorElement = new ShapeElement(this, ResourceTable.Graphic_background_textfild_uncolor);
                    textUtils.animatorField(mIpText, false, maxsize);
                }
                mEditHint.setTextColor(new Color(Color.getIntColor(color)));
                mIpText.setTextColor(new Color(Color.getIntColor(color)));
                mIpEt.setBasement(errorElement);
                break;
            case ResourceTable.Id_port:
                if (isFocus) {
                    color = mSelectColor;
                    errorElement = new ShapeElement(this, ResourceTable.Graphic_background_textfild_color);
                    if (mPortText.getVisibility() == Component.VISIBLE) {
                        textUtils.animatorField(mPortText, true, mSize);
                    }
                } else {
                    color = mColorUn;
                    errorElement = new ShapeElement(this, ResourceTable.Graphic_background_textfild_uncolor);
                    textUtils.animatorField(mPortText, false, maxsize);
                }
                mPortHint.setTextColor(new Color(Color.getIntColor(color)));
                mPortText.setTextColor(new Color(Color.getIntColor(color)));
                mPortEt.setBasement(errorElement);
                break;
            case ResourceTable.Id_pulse_frequency:
                if (isFocus) {
                    color = mSelectColor;
                    errorElement = new ShapeElement(this, ResourceTable.Graphic_background_textfild_color);
                    if (mPulseText.getVisibility() == Component.VISIBLE) {
                        textUtils.animatorField(mPulseText, true, mSize);
                    }
                } else {
                    color = mColorUn;
                    errorElement = new ShapeElement(this, ResourceTable.Graphic_background_textfild_uncolor);
                    textUtils.animatorField(mPulseText, false, maxsize);
                }
                mPulseHint.setTextColor(new Color(Color.getIntColor(color)));
                mPulseText.setTextColor(new Color(Color.getIntColor(color)));
                mFrequencyEt.setBasement(errorElement);
                break;
            default:
                break;
        }
    }
}
